import datetime
import json
import os

import Logger
import Util
from ExcelData import ExcelData, TableData, FieldData, FieldType, EData, LoadConfig, IndexType, Platform, Union
from typing import List, Dict

from ICodeGenerator import ICodeGenerator


class CodeJavaGenerator(ICodeGenerator):
    CODE_NAME: str = "Java"
    CODE_FILE_EXT_NAME: str = ".java"

    TEMPLATE_VO_FILEPATH: str = "template\\JavaVoClass.template"
    TEMPLATE_EASY_ACCESS_FILEPATH: str = "template\\JavaEasyAccessClass.template"
    TEMPLATE_LIB_CONSTANT: str = "template\\JavaLibConstantClass.template"

    VO_FIELD_COMMENT: str = """    
    /**
    * [%Comment%]
    **/"""
    VO_FIELD_DEFINE: str = """    private [%FieldType%] [%FieldName%];"""
    VO_FIELD_GET: str = """    public [%FieldType%] [%Get%]() { return [%FieldName%]; }"""
    VO_FIELD_SET: str = """    public void [%Set%]([%FieldType%] [%FieldName%]) { this.[%FieldName%] = [%FieldName%]; }"""
    VO_TOSTRING: str = """
    @Override
    public String toString() {
        return "[%ClassName%]([%SheetName%]){" +
                [%ToStringContent%]
                '}';
    }
    """
    VO_PARSE_FROM_JSON: str = """
    /**
     * 从json中加载数据
     */
    @Override
    public void preload() {        
        [%ParseCode%]
    }
    """
    VO_GET_OTHER_VO: str = """
    /**
     * [%Comment%] (实体)
     * @return
     */
    public [%OthClassName%] [%GetMethod%]() {
        return getLib().[%OthMethod%]([%KeyName%]);
    }
    """
    CONST_LIB_CONSTANT_STRING: str = """
    /**
     * [%Comment%]
     */
    public static final String [%ConstName%] = "[%ConstValue%]";
    """
    CONST_LIB_CONSTANT_LIST: str = """
    /**
     * [%Comment%] dataFullName Relation: [%DataName%]() -> List<[%ClassName%]>
     */
    public static final String [%ConstName%] = "[%ConstValue%]";
    """
    CONST_LIB_CONSTANT_MAP: str = """
    /**
     * [%Comment%] dataFullName Relation: [%DataName%]([%KeyType%] [%KeyName%]) -> [%ClassName%]
     */
    public static final String [%ConstName%] = "[%ConstValue%]";
    """
    CONST_LIB_CONSTANT_MAP2LIST: str = """
    /**
     * [%Comment%] dataFullName Relation: [%DataName%]([%KeyType%] [%KeyName%]) -> List<[%ClassName%]>
     */
    public static final String [%ConstName%] = "[%ConstValue%]";
    """
    CONST_LIB_CONSTANT_MULT_KEY_MAP: str = """
    /**
     * [%Comment%] dataFullName Relation: [%DataName%]([%MultKeysAndTypes%]) -> [%ClassName%]
     */
    public static final String [%ConstName%] = "[%ConstValue%]";
    """
    LOAD_METHOD_LIST: str = """
    /**
     * [%Comment%]
     * @return [%ClassName%]
     */
    public List<[%ClassName%]> [%DataName%]() {
        return (List<[%ClassName%]>) maps.get("[%DataFullName%]");
    }"""
    LOAD_METHOD_MAP: str = """
    /**
     * [%Comment%]
     * @param [%KeyName%]
     * @return [%ClassName%]
     */
    public [%ClassName%] [%DataName%]([%KeyType%] [%KeyName%]) {
        Map<[%KeyTypeWapper%], [%ClassName%]> data = (Map<[%KeyTypeWapper%], [%ClassName%]>) maps.get("[%DataFullName%]");
        return data == null ? null : data.get([%KeyName%]);
    }"""
    LOAD_METHOD_MAP2LIST: str = """
    /**
     * [%Comment%]
     * @param [%KeyName%]
     * @return [%ClassName%]
     */
    public List<[%ClassName%]> [%DataName%]([%KeyType%] [%KeyName%]){
        Map<[%KeyTypeWapper%], List<[%ClassName%]>> data = (Map<[%KeyTypeWapper%], List<[%ClassName%]>>) maps.get("[%DataFullName%]");
        return data == null ? null : data.get([%KeyName%]);
    }"""
    LOAD_METHOD_MULT_KEY_MAP: str = """
    /**
     * [%Comment%]
     * @param [%MultKeysAndTypes%]
     * @return [%ClassName%]
     */
    public [%ClassName%] [%DataName%]([%MultKeysAndTypes%]) {
        Map<String, [%ClassName%]> data = (Map<String, [%ClassName%]>) maps.get("[%DataFullName%]");
        return data == null ? null : data.get([%QueryMultKeys%]);
    }
    """

    voTemplate: str = ""
    """启动时加载进内存的Vo代码模板"""
    libConstTemplate: str = ""
    """启动时加载进内存的常量表代码模板"""
    easyAccessTemplate: str = ""
    """启动时加载进内存的快捷读取类代码模板"""
    voGenerateCodePath: str = ""
    """Vo类生成目录"""

    def getFieldType(self, fieldType: FieldType):
        """
        获取java字段类型文本
        :param fieldType: FieldType
        :return: getFieldType(FieldType) --> str 对应代码的类型字符串
        """
        if fieldType is FieldType.Integer:
            return "int"
        elif fieldType is FieldType.String:
            return "String"
        elif fieldType is FieldType.Boolean:
            return "boolean"
        elif fieldType is FieldType.Float:
            return "float"
        elif fieldType is FieldType.Long:
            return "long"
        elif fieldType.isArrTypeOrMap():
            return "String"

    def getFieldTypeWrapper(self, fieldType: FieldType):
        """
        获取java字段类型文本
        :param fieldType: FieldType
        :return: getFieldType(FieldType) --> str 对应代码的类型字符串
        """
        if fieldType is FieldType.Integer:
            return "Integer"
        elif fieldType is FieldType.String:
            return "String"
        elif fieldType is FieldType.Boolean:
            return "Boolean"
        elif fieldType is FieldType.Float:
            return "Float"
        elif fieldType is FieldType.Long:
            return "Long"
        elif fieldType is FieldType.Double:
            return "Double"
        elif fieldType.isArrTypeOrMap():
            return "String"


    def getFieldRealTypeArrOrMap(self, fieldType: FieldType):
        """
        获取Laya字段类型文本(數組或Map字段專用)
        :param fieldType: FieldType
        :return: getFieldType(FieldType) --> str 对应代码的类型字符串
        """
        if (fieldType is FieldType.intMap):
            return "Integer"
        elif (fieldType is FieldType.strMap):
            return "String"
        elif (fieldType is FieldType.floatMap):
            return "Float"
        elif (fieldType is FieldType.doubleMap):
            return "Double"
        elif (fieldType is FieldType.boolMap):
            return "Boolean"
        elif (fieldType is FieldType.intMap or
              fieldType is FieldType.intArrComma or
              fieldType is FieldType.intArrSemicolon or
              fieldType is FieldType.intArrHashMark or
              fieldType is FieldType.intArrVertiBar):
            return "Integer"
        elif (fieldType is FieldType.strMap or
              fieldType is FieldType.strArrComma or
              fieldType is FieldType.strArrSemicolon or
              fieldType is FieldType.strArrHashMark or
              fieldType is FieldType.strArrVertiBar):
            return "String"
        elif (fieldType is FieldType.floatMap or
              fieldType is FieldType.floatArrComma or
              fieldType is FieldType.floatArrSemicolon or
              fieldType is FieldType.floatArrHashMark or
              fieldType is FieldType.floatArrVertiBar):
            return "Float"
        elif (fieldType is FieldType.boolArrComma or
              fieldType is FieldType.boolArrHashMark or
              fieldType is FieldType.boolArrVertiBar or
              fieldType is FieldType.boolArrSemicolon):
            return "Boolean"
        elif (fieldType is FieldType.int2DMap):
            return "Integer"
        elif (fieldType is FieldType.str2DMap):
            return "String"
        elif (fieldType is FieldType.float2DMap):
            return "Float"
        elif (fieldType is FieldType.double2DMap):
            return "Double"
        elif (fieldType is FieldType.bool2DMap):
            return "Boolean"
        elif (fieldType is FieldType.int2DListVertiBar):
            return "Integer"
        elif (fieldType is FieldType.str2DListVertiBar):
            return "String"
        elif (fieldType is FieldType.float2DListVertiBar):
            return "Float"
        elif (fieldType is FieldType.bool2DListVertiBar):
            return "Boolean"

    def getFieldTypeArrOrMap(self, fieldType: FieldType):
        """
        获取Laya字段类型文本(數組或Map字段專用)
        :param fieldType: FieldType
        :return: getFieldType(FieldType) --> str 对应代码的类型字符串
        """
        if (fieldType is FieldType.intMap):
            return "Map<String, Integer>"
        elif (fieldType is FieldType.strMap):
            return "Map<String, String>"
        elif (fieldType is FieldType.floatMap):
            return "Map<String, Float>"
        elif (fieldType is FieldType.doubleMap):
            return "Map<String, Double>"
        elif (fieldType is FieldType.boolMap):
            return "Map<String, Boolean>"
        elif (fieldType is FieldType.intArrComma or
              fieldType is FieldType.intArrSemicolon or
              fieldType is FieldType.intArrHashMark or
              fieldType is FieldType.intArrVertiBar):
            return "Integer[]"
        elif (fieldType is FieldType.strArrComma or
              fieldType is FieldType.strArrSemicolon or
              fieldType is FieldType.strArrHashMark or
              fieldType is FieldType.strArrVertiBar):
            return "String[]"
        elif (fieldType is FieldType.floatArrComma or
              fieldType is FieldType.floatArrSemicolon or
              fieldType is FieldType.floatArrHashMark or
              fieldType is FieldType.floatArrVertiBar):
            return "Float[]"
        elif (fieldType is FieldType.boolArrComma or
              fieldType is FieldType.boolArrHashMark or
              fieldType is FieldType.boolArrVertiBar or
              fieldType is FieldType.boolArrSemicolon):
            return "Boolean[]"
        elif (fieldType is FieldType.int2DMap):
            return "Map<String, Integer[]>"
        elif (fieldType is FieldType.str2DMap):
            return "Map<String, String[]>"
        elif (fieldType is FieldType.float2DMap):
            return "Map<String, Float[]>"
        elif (fieldType is FieldType.double2DMap):
            return "Map<String, Double[]>"
        elif (fieldType is FieldType.bool2DMap):
            return "Map<String, Boolean[]>"
        elif (fieldType is FieldType.int2DListVertiBar):
            return "Integer[][]"
        elif (fieldType is FieldType.str2DListVertiBar):
            return "String[][]"
        elif (fieldType is FieldType.float2DListVertiBar):
            return "Float[][]"
        elif (fieldType is FieldType.bool2DListVertiBar):
            return "Boolean[][]"
    @property
    def loadTemplate(self):
        """
        加载代码模板
        :return: loadTemplate() --> bool 是否成功加载
        """
        log: Logger = Logger.Logger()
        edata: EData = EData()
        voPath = edata.setting.genDirJavaFile
        if not voPath.endswith("\\"):
            voPath += "\\"
        self.voGenerateCodePath = voPath
        if os.path.isdir(self.voGenerateCodePath) == False:
            log.logError("导出" + self.CODE_NAME + "的VO目录不存在：" + voPath)
            return False
        # 读文件加载Vo模板
        if not os.path.isfile(self.TEMPLATE_VO_FILEPATH):
            log.logError(self.CODE_NAME + "Vo模板文件打开失败，请检查文件是否存在：" + self.TEMPLATE_VO_FILEPATH)
            return False
        try:
            f = open(self.TEMPLATE_VO_FILEPATH, "r", encoding='utf8')
            self.voTemplate = f.read()
        except UnicodeDecodeError as e:
            log.logError(self.CODE_NAME + "Vo模板文件打开失败，文件必须为Utf-8格式：" + self.TEMPLATE_VO_FILEPATH)
            return False
        finally:
            f.close()
        # 读文件加载Loader模板
        if not os.path.isfile(self.TEMPLATE_EASY_ACCESS_FILEPATH):
            log.logError(
                self.CODE_NAME + "Loader模板文件打开失败，请检查文件是否存在：" + self.TEMPLATE_EASY_ACCESS_FILEPATH)
            return False
        try:
            f = open(self.TEMPLATE_EASY_ACCESS_FILEPATH, "r", encoding='utf8')
            self.easyAccessTemplate = f.read()
        except UnicodeDecodeError as e:
            log.logError(
                self.CODE_NAME + "Loader模板文件打开失败，文件必须为Utf-8格式：" + self.TEMPLATE_EASY_ACCESS_FILEPATH)
            return False
        finally:
            f.close()
        # 读文件加载常量表模板
        if not os.path.isfile(self.TEMPLATE_LIB_CONSTANT):
            log.logError(self.CODE_NAME + "Loader模板文件打开失败，请检查文件是否存在：" + self.TEMPLATE_LIB_CONSTANT)
            return False
        try:
            f = open(self.TEMPLATE_LIB_CONSTANT, "r", encoding='utf8')
            self.libConstTemplate = f.read()
        except UnicodeDecodeError as e:
            log.logError(self.CODE_NAME + "Loader模板文件打开失败，文件必须为Utf-8格式：" + self.TEMPLATE_LIB_CONSTANT)
            return False
        finally:
            f.close()

        return True

    def getterName(self, field: FieldData):
        s: str = field.fieldKey
        if field.fieldType is FieldType.Boolean:
            if s.lower().startswith("is"):
                sf = s[:len(s) - 2]
                ss = s[2:].lower()
                s = sf + ss
            else:
                ss = list(s)
                ss[0] = ss[0].upper()
                s = "is" + "".join(ss)
        else:
            ss = list(s)
            ss[0] = ss[0].upper()
            s = "get" + "".join(ss)
        return s

    def setterName(self, field: FieldData):
        s: str = field.fieldKey
        if field.fieldType is FieldType.Boolean:
            if s.lower().startswith("is"):
                s = s[2:]
        ss = list(s)
        ss[0] = ss[0].upper()
        s = "set" + "".join(ss)
        return s


    def generateCode(self, exportDir: str, exportVersion: str, exportTime: datetime):
        """
        生成代码 在执行前，必须先执行loadTemplate()
        :param exportVersion: str 发布版本号
        :param exportTime: datetime 发布日期
        :return: bool 是否顺利
        """
        edata: EData = EData()
        log: Logger = Logger.Logger()

        if not exportDir.endswith("\\"):
            exportDir += "\\"

        # # 删除之前生成的.java文件
        # filelist = os.listdir(self.voGenerateCodePath)
        # for filename in filelist:
        #     filepath = self.voGenerateCodePath + filename
        #     if os.path.isfile(filepath):
        #         if filename.endswith(self.CODE_FILE_EXT_NAME):
        #             os.remove(filepath)
        #             print("删除旧文件：" + filepath)

        # 开始生成每个【实体类】
        for excel in edata.excelDatas.selectedDatas.values():
            for table in excel.tables.values():
                # 检查是否已勾选导出
                if not table.isListChecked:
                    continue
                # 检查是否配置了Java导出
                if Platform.Java not in table.exportLibPlatforms.values():
                    continue
                fieldDefineCode = ""
                fieldGetSetCode = ""
                methodToGetOtherVoCode = ""
                toStringContentCode = ""
                parseFromJsonCode = ""  # 从json加载数据代码
                importContent = ""  # 引用导入import代码
                importClassMap: dict[str] = dict()
                for field in table.fields:

                    # 生成ToString代码
                    if toStringContentCode != "":
                        toStringContentCode += "                \", "
                    else:
                        toStringContentCode += "\""

                    if field.fieldType.isArrTypeOrMap():
                        # 從模板創建字段定義代碼
                        fieldCode = self.genListOrMapFieldCode(field)
                        fieldDefineCode += fieldCode
                        # 从模板替换创建GetterSetter
                        getterSetterCode = self.genListOrMapGetterSetterCode(field)
                        fieldGetSetCode += getterSetterCode
                        # 從模板創建toString代碼
                        toStringCode = self.genListOrMapToStringCode(field)
                        toStringContentCode += toStringCode

                        #检查是否导入了正则所需要的import
                        importClassItem = importClassMap.get("regex")
                        if importClassItem is None:
                            importContent += "import java.util.regex.Matcher;\n"
                            importContent += "import java.util.regex.Pattern;\n"
                            importClassMap["regex"] = "regex"
                        # 生成从json读取代码
                        if field.fieldType.isMapType():
                            parseCode = self.genMapParseCode(field)
                            # 添加到import代码
                            importClassItem = importClassMap.get("Map")
                            if importClassItem is None:
                                importContent += "import java.util.Map;\n"
                                importContent += "import java.util.HashMap;\n"
                                importClassMap["Map"] = "Map"
                        else:
                            parseCode = self.genListParseCode(field)
                        parseFromJsonCode += parseCode
                    else:
                        # 從模板創建字段定義代碼
                        fieldCode = self.genNormalFieldCode(field)
                        fieldDefineCode += fieldCode
                        # 从模板替换创建GetterSetter
                        getterSetterCode = self.genNormalGetterSetterCode(field)
                        fieldGetSetCode += getterSetterCode
                        # 從模板創建toString代碼
                        toStringCode = self.genNormalToStringCode(field)
                        toStringContentCode += toStringCode
                        # 生成从json读取代码
                        parseCode = self.genNormalParseCode(field)
                        parseFromJsonCode += parseCode

                # 获取“字段关联”功能(其他类实体快捷调用)的代码
                for myCfg in table.loadConfigs:
                    if Platform.Java not in myCfg.exportPlatforms.values():
                        continue
                    for union in myCfg.unions:
                        # 有配置关联才会进来循环
                        uTable: TableData = None
                        # 尝试找出对应的关联的表
                        for uExcel in edata.excelDatas.selectedDatas.values():
                            for utable2 in uExcel.tables.values():
                                if union.unionTableName == utable2.sheetName:
                                    uTable = utable2
                        if uTable is None:
                            log.logError(
                                "严重错误！ [" + table.sheetName + "]配置表'字段关联'中找不到关联字段的表：" + union.unionTableName)
                            return False

                        # 检查目标表下有没有做对应绑定字段索引的方式
                        uCfg: LoadConfig = None
                        for uCfg2 in uTable.loadConfigs:
                            if uCfg2.type == IndexType.List or uCfg2.type == IndexType.MultKeyMap:
                                continue
                            if uCfg2.keyFields[0].fieldKey == union.unionKey:
                                uCfg = uCfg2
                        if uCfg is None:
                            log.logError(
                                "严重错误！ [" + table.sheetName + "]配置表[" + union.unionTableName + "]'字段关联'中找不到关联字段的键：" + union.unionKey)
                            return False
                        # 检查绑定自身字段是否存在
                        myField: FieldData = None
                        for myField2 in table.fields:
                            if myField2.fieldKey == union.myKey:
                                myField = myField2
                        if myField is None:
                            log.logError(
                                "严重错误！ [" + table.sheetName + "]配置表'字段关联'中, 本表没有该字段：" + union.myKey)
                            return False
                        if uCfg.keyFields[0].fieldType != myField.fieldType:
                            log.logError(
                                "严重错误！ [" + table.sheetName + "]配置表'字段关联'中, 本表字段：" + union.myKey + "与目标字段类型不一致!")
                            return False
                        # 全部检验完毕，开始生产绑定数据
                        tmpOther: str = self.VO_GET_OTHER_VO
                        tmpOther = tmpOther.replace("[%Comment%]",
                                                    uTable.sheetName + "  关联：" + union.myKey + "->" + uTable.getClassname() + "." + union.unionKey)
                        tmpOther = tmpOther.replace("[%OthSheetName%]", "对应资料Vo")
                        tmpOther = tmpOther.replace("[%OthClassName%]", uTable.getClassname())
                        tmpOther = tmpOther.replace("[%OthMethod%]", uCfg.getDataName(uTable))
                        tmpOther = tmpOther.replace("[%KeyName%]", myField.fieldKey)
                        ss = list(myField.fieldKey);
                        ss[0] = ss[0].upper()
                        upperFieldKey = "get" + uTable.getClassname() + "By" + ("".join(ss))
                        tmpOther = tmpOther.replace("[%GetMethod%]", upperFieldKey)

                        methodToGetOtherVoCode += tmpOther

                # 提取类名
                className: str = table.getClassname()
                # 文件名
                filename: str = className + self.CODE_FILE_EXT_NAME
                # toString生成模板
                toStringCode: str = self.VO_TOSTRING
                toStringCode = toStringCode.replace("[%ClassName%]", className)
                toStringCode = toStringCode.replace("[%SheetName%]", table.sheetName)
                toStringCode = toStringCode.replace("[%ToStringContent%]", toStringContentCode.strip("\n"))
                # 从json读取数据模板
                parseFromJsonContent: str = self.VO_PARSE_FROM_JSON
                if len(parseFromJsonCode) > 0: # 如果需要预处理代码，则加入通用正则处理patten的创建代码
                     parseFromJsonContent = self.PATTERN_CODE + parseFromJsonContent;
                parseFromJsonContent = parseFromJsonContent.replace("[%ParseCode%]", parseFromJsonCode)
                # 类实体生成
                classConent: str = fieldDefineCode
                classConent += "\n\n"
                classConent += fieldGetSetCode
                classConent += methodToGetOtherVoCode
                classConent += parseFromJsonContent
                classConent += toStringCode
                # 类模板替换
                voCode: str = self.voTemplate
                voCode = voCode.replace("[%Comment%]", table.sheetName)
                voCode = voCode.replace("[%Author%]", "Generate By Excel2Json")
                # voCode = voCode.replace("[%Datetime%]", Util.getDateTimeString(exportTime))
                # voCode = voCode.replace("[%Version%]", exportVersion)
                voCode = voCode.replace("[%ClassName%]", className)
                voCode = voCode.replace("[%Content%]", classConent)
                voCode = voCode.replace("[%Import%]", importContent)
                # 保存文件
                filepath = exportDir + filename  # self.voGenerateCodePath + filename
                f = open(filepath, 'w', encoding='utf8')
                f.write(voCode)
                f.close()
                log.logSuccess("生成Java实体类：" + filename)

        # 开始生成【工具类-快捷读取】
        constantGroupDict: dict = dict()
        loaderContent: str = ""
        fieldConstContent: str = ""
        for excel in edata.excelDatas.selectedDatas.values():
            for table in excel.tables.values():
                # 检查是否已勾选导出
                if not table.isListChecked:
                    continue

                for cfg in table.loadConfigs:
                    if Platform.Java not in cfg.exportPlatforms.values():
                        continue
                    # 根据loadConfig配置生成数据表
                    code: str = ""
                    fieldCode: str = ""
                    multKeysAndTypes: str = ""
                    multKeysQuery: str = ""
                    if cfg.type is IndexType.List:
                        code = self.LOAD_METHOD_LIST
                        fieldCode = self.CONST_LIB_CONSTANT_LIST
                    elif cfg.type is IndexType.Map:
                        code = self.LOAD_METHOD_MAP
                        fieldCode = self.CONST_LIB_CONSTANT_MAP
                    elif cfg.type is IndexType.Map2List:
                        code = self.LOAD_METHOD_MAP2LIST
                        fieldCode = self.CONST_LIB_CONSTANT_MAP2LIST
                    elif cfg.type is IndexType.MultKeyMap:
                        code = self.LOAD_METHOD_MULT_KEY_MAP
                        fieldCode = self.CONST_LIB_CONSTANT_MULT_KEY_MAP
                        # 计算多个参数的MultKeyType
                        for field in cfg.keyFields:
                            multKeysAndTypes += self.getFieldType(field.fieldType) + " "
                            multKeysAndTypes += field.fieldKey + ", "
                            multKeysQuery += field.fieldKey + " + \"_\" + "
                        if len(multKeysAndTypes) > 2:
                            # 减掉最后的', '
                            multKeysAndTypes = multKeysAndTypes[:-2]
                        if len(multKeysQuery) > 3:
                            # 减掉最后的' + '
                            multKeysQuery = multKeysQuery[:-3]

                    comment: str = "表[" + table.sheetName + ":" + table.jsonName + "] " + cfg.getDataFullName(table)

                    code = code.replace("[%ClassName%]", table.getClassname())
                    code = code.replace("[%DataName%]", cfg.getDataName(table))
                    code = code.replace("[%DataFullName%]", cfg.getDataFullName(table))
                    # code中用的基础类型(如int)
                    code = code.replace("[%KeyType%]", self.getFieldType(cfg.keyFields[0].fieldType))
                    # code中用的装饰类包装的基础类型（如Integer）
                    code = code.replace("[%KeyTypeWapper%]", self.getFieldTypeWrapper(cfg.keyFields[0].fieldType))
                    code = code.replace("[%KeyName%]", cfg.keyFields[0].fieldKey)
                    code = code.replace("[%MultKeysAndTypes%]", multKeysAndTypes)
                    code = code.replace("[%QueryMultKeys%]", multKeysQuery)
                    code = code.replace("[%Comment%]", comment)
                    loaderContent += code

                    # 把出现的group都记录下来
                    if cfg.group != "":
                        subGroupList: List
                        subGroupList = constantGroupDict.get(cfg.group)
                        if subGroupList is None:
                            subGroupList = []
                            constantGroupDict[cfg.group] = subGroupList
                        subGroupList.append([cfg.getDataFullName(table), table.getClassname(), table.sheetName])

                    # 常量表字段模板替换
                    fieldCode = fieldCode.replace("[%ClassName%]", table.getClassname())
                    fieldCode = fieldCode.replace("[%DataName%]", cfg.getDataName(table))
                    fieldCode = fieldCode.replace("[%KeyType%]", self.getFieldType(cfg.keyFields[0].fieldType))
                    fieldCode = fieldCode.replace("[%KeyName%]", cfg.keyFields[0].fieldKey)
                    fieldCode = fieldCode.replace("[%MultKeysAndTypes%]", multKeysAndTypes)
                    constName: str = edata.setting.libTablePrefix + cfg.getConstantDataFullName(table)
                    constName = constName[:-1]
                    fieldCode = fieldCode.replace("[%ConstName%]", constName)
                    fieldCode = fieldCode.replace("[%ConstValue%]", cfg.getDataFullName(table))
                    fieldCode = fieldCode.replace("[%Comment%]", comment)
                    fieldConstContent += fieldCode

        # 简易读取类模板替换
        loaderCode: str = self.easyAccessTemplate
        easyAccessClassName: str = edata.setting.genLibEasyAccessClass
        easyAccessFileName: str = edata.setting.genLibEasyAccessClass + self.CODE_FILE_EXT_NAME
        loaderCode = loaderCode.replace("[%Author%]", "Generate By Excel2Json")
        # loaderCode = loaderCode.replace("[%Datetime%]", Util.getDateTimeString(exportTime))
        # loaderCode = loaderCode.replace("[%Version%]", exportVersion)
        loaderCode = loaderCode.replace("[%ClassName%]", easyAccessClassName)
        loaderCode = loaderCode.replace("[%Content%]", loaderContent)
        # 保存简易读取类文件
        filepath = exportDir + easyAccessFileName  # self.voGenerateCodePath + easyAccessFileName
        f = open(filepath, 'w', encoding='utf8')
        f.write(loaderCode)
        f.close()
        log.logSuccess("生成Java快捷加载类：" + easyAccessFileName)

        # 开始生成【常量类】
        # 提取Group信息
        constantContentCode: str = ""
        for gKey, gVal in constantGroupDict.items():
            groupKey: str = gKey
            subGroupList: List = gVal
            code: str = self.CONST_LIB_CONSTANT_STRING
            code = code.replace("[%ConstName%]", (edata.setting.groupPrefix + groupKey).upper())
            code = code.replace("[%ConstValue%]", edata.setting.groupPrefix + groupKey)
            comment: str = "组别[" + groupKey + "] 包含表如下："
            for gdata in subGroupList:
                dataFullName = gdata[0]
                classname = gdata[1]
                sheetName = gdata[2]
                comment += "<BR/>[" + sheetName + " " + classname + "] -> " + dataFullName + "();"
            code = code.replace("[%Comment%]", comment)
            constantContentCode += code

        constantContentCode += fieldConstContent

        # 常量表模板替换
        constCode: str = self.libConstTemplate
        libConstantClassName: str = edata.setting.genLibConstantClass
        libConstantFileName: str = edata.setting.genLibConstantClass + self.CODE_FILE_EXT_NAME
        constCode = constCode.replace("[%Author%]", "Generate By Excel2Json")
        # constCode = constCode.replace("[%Datetime%]", Util.getDateTimeString(exportTime))
        # constCode = constCode.replace("[%Version%]", exportVersion)
        constCode = constCode.replace("[%ClassName%]", libConstantClassName)
        constCode = constCode.replace("[%Content%]", constantContentCode)

        # 保存常量表文件
        filepath = exportDir + libConstantFileName  # self.voGenerateCodePath + libConstantFileName
        f = open(filepath, 'w', encoding='utf8')
        f.write(constCode)
        f.close()
        log.logSuccess("生成Java快捷加载类：" + libConstantFileName)

        return True


    def genNormalFieldCode(self, field: FieldData):
        code = ""
        code += self.VO_FIELD_COMMENT.replace("[%Comment%]", field.fieldName) + "\n"
        tmpField: str = self.VO_FIELD_DEFINE
        tmpField = tmpField.replace("[%FieldType%]", self.getFieldType(field.fieldType))
        tmpField = tmpField.replace("[%FieldName%]", field.fieldKey)
        code += tmpField + "\n"
        return code

    def genListOrMapFieldCode(self, field: FieldData):
        code = ""
        code += self.VO_FIELD_COMMENT.replace("[%Comment%]", field.fieldName) + "\n"
        tmpField: str = self.VO_FIELD_DEFINE
        tmpField = tmpField.replace("[%FieldType%]", self.getFieldType(field.fieldType))
        tmpField = tmpField.replace("[%FieldName%]", field.fieldKey + "OrgStr")
        code += tmpField + "\n"
        code += self.VO_FIELD_COMMENT.replace("[%Comment%]", field.fieldName) + "\n"
        tmpField: str = self.VO_FIELD_DEFINE
        tmpField = tmpField.replace("[%FieldType%]", self.getFieldTypeArrOrMap(field.fieldType))
        tmpField = tmpField.replace("[%FieldName%]", field.fieldKey)
        code += tmpField + "\n"
        return code


    def genNormalGetterSetterCode(self, field: FieldData):
        code = ""
        # 从模板替换创建Getter
        code += self.VO_FIELD_COMMENT.replace("[%Comment%]", field.fieldName)
        code += "\n"
        tmpGetter: str = self.VO_FIELD_GET
        tmpGetter = tmpGetter.replace("[%FieldType%]", self.getFieldType(field.fieldType))
        tmpGetter = tmpGetter.replace("[%FieldName%]", field.fieldKey)
        tmpGetter = tmpGetter.replace("[%Get%]", self.getterName(field))
        code += tmpGetter
        code += "\n"
        # 从模板替换创建Setter
        tmpSetter: str = self.VO_FIELD_SET
        tmpSetter = tmpSetter.replace("[%FieldType%]", self.getFieldType(field.fieldType))
        tmpSetter = tmpSetter.replace("[%FieldName%]", field.fieldKey)
        tmpSetter = tmpSetter.replace("[%Set%]", self.setterName(field))
        code += tmpSetter
        code += "\n"
        return code


    def genListOrMapGetterSetterCode(self, field: FieldData):
        code = ""
        # 从模板替换创建Getter
        code += self.VO_FIELD_COMMENT.replace("[%Comment%]", field.fieldName)
        code += "\n"
        tmpGetter: str = self.VO_FIELD_GET
        tmpGetter = tmpGetter.replace("[%FieldType%]", self.getFieldType(field.fieldType))
        tmpGetter = tmpGetter.replace("[%FieldName%]", field.fieldKey + "OrgStr")
        tmpGetter = tmpGetter.replace("[%Get%]", self.getterName(field) + "OrgStr")
        code += tmpGetter
        code += "\n"
        # 从模板替换创建Setter
        tmpSetter: str = self.VO_FIELD_SET
        tmpSetter = tmpSetter.replace("[%FieldType%]", self.getFieldType(field.fieldType))
        tmpSetter = tmpSetter.replace("[%FieldName%]", field.fieldKey + "OrgStr")
        tmpSetter = tmpSetter.replace("[%Set%]", self.setterName(field) + "OrgStr")
        code += tmpSetter
        code += "\n"
        tmpGetter: str = self.VO_FIELD_GET
        tmpGetter = tmpGetter.replace("[%FieldType%]", self.getFieldTypeArrOrMap(field.fieldType))
        tmpGetter = tmpGetter.replace("[%FieldName%]", field.fieldKey)
        tmpGetter = tmpGetter.replace("[%Get%]", self.getterName(field))
        code += tmpGetter
        code += "\n"
        return code

    def genNormalToStringCode(self, field: FieldData):
        code = ""
        # 生成ToString代码
        if field.fieldType == FieldType.String:
            code += field.fieldKey + "='\" + this." + field.fieldKey + " + '\\'' + \n"
        else:
            code += field.fieldKey + "=\" + this." + field.fieldKey + " + \n"
        return code

    def genListOrMapToStringCode(self, field: FieldData):
        code = ""
        # 生成ToString代码
        if field.fieldType == FieldType.String:
            code += field.fieldKey + "='\" + this." + field.fieldKey + "OrgStr" + " + '\\'' + \n"
        else:
            code += field.fieldKey + "=\" + this." + field.fieldKey + "OrgStr" + " + \n"
        return code

    def genNormalParseCode(self, field: FieldData):
        code = ""
        # code += "\n        this." + field.fieldKey + " = json[\"" + field.fieldKey + "\"]"
        return code


    PATTERN_CODE:str = """
    /**
     * 通用去回车正则 
    **/
    Pattern pattern = Pattern.compile("[\\r\\n]+", Pattern.CASE_INSENSITIVE);        
    """

    LIST_PARSE_CODE: str = """
    if(this.[%FieldName%]OrgStr != null && !"".equals(this.[%FieldName%]OrgStr)){
        Matcher [%FieldListName%]Matcher = pattern.matcher(this.[%FieldName%]OrgStr);
        String[] [%FieldListName%] = [%FieldListName%]Matcher.replaceAll("").split("[%SplitCode%]");
        this.[%FieldName%] = new [%FieldType%][[%FieldListName%].length];        
        for (int i = 0; i < [%FieldListName%].length; i++) {
            if ([%FieldListName%][i].length() > 0){
                this.[%FieldName%][i] = [%ParseFun%];
            }
        }
    }
    """

    LIST_2D_PARSE_CODE: str = """
    if(this.[%FieldName%]OrgStr != null && !"".equals(this.[%FieldName%]OrgStr)){
        Matcher [%FieldListName%]Matcher = pattern.matcher(this.[%FieldName%]OrgStr);
        String[] [%FieldListName%] = [%FieldListName%]Matcher.replaceAll("").split("[%SplitCode%]");
        this.[%FieldName%] = new [%FieldType%][[%FieldListName%].length][];        
        for (int i = 0; i < [%FieldListName%].length; i++) {
            if ([%FieldListName%][i].length() > 0){
                String[] ts = [%FieldListName%][i].split(",");
                [%FieldType%][] ls = new [%FieldType%][ts.length];
                for(int j=0; j<ts.length;j++){
                    ls[j] = [%ParseFun2D%];
                };
                this.[%FieldName%][i] = ls;
            }
        }
    }
    """
    def genListParseCode(self, field: FieldData):
        code = ""
        # code += "\n        this." + field.fieldKey + "OrgStr" + " = json[\"" + field.fieldKey + "\"]"
        tmpSetter: str
        if field.fieldType.is2DListType():
            tmpSetter = self.LIST_2D_PARSE_CODE
        else:
            tmpSetter = self.LIST_PARSE_CODE

        fieldListName = field.fieldKey + "OrgStrList"
        tmpSetter = tmpSetter.replace("[%FieldType%]", self.getFieldRealTypeArrOrMap(field.fieldType))
        tmpSetter = tmpSetter.replace("[%FieldName%]", field.fieldKey)
        tmpSetter = tmpSetter.replace("[%FieldListName%]", fieldListName)

        if field.fieldType.isSplitComma():
            tmpSetter = tmpSetter.replace("[%SplitCode%]", ",")
        elif field.fieldType.isSplitVertiBar():
            tmpSetter = tmpSetter.replace("[%SplitCode%]", "\\\\|")
        elif field.fieldType.isSplitHashMark():
            tmpSetter = tmpSetter.replace("[%SplitCode%]", "#")
        elif field.fieldType.isSplitSemicolon():
            tmpSetter = tmpSetter.replace("[%SplitCode%]", ";")

        if field.fieldType.isArrIntType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "Float.valueOf(" + fieldListName + "[i].trim()).intValue()")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "Float.valueOf(ts[j].trim()).intValue()")
        elif field.fieldType.isArrFloatType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "Float.valueOf(" + fieldListName + "[i].trim())")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "Float.valueOf(ts[j].trim())")
        elif field.fieldType.isArrStrType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", fieldListName + "[i]")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "ts[j]")
        elif field.fieldType.isArrBoolType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "\"1\".equals(" + fieldListName + "[i].trim()) || "
                                          + "\"t\".equals(" + fieldListName + "[i].trim().toLowerCase()) || "
                                          + "\"true\".equals(" + fieldListName + "[i].trim().toLowerCase())")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "\"1\".equals(ts[j].trim()) || "
                                          + "\"t\".equals(ts[j].trim().toLowerCase()) || "
                                          + "\"true\".equals(ts[j].trim().toLowerCase())")


        code += tmpSetter
        return code

    MAP_PARSE_CODE: str = """
    if(this.[%FieldName%]OrgStr != null && !"".equals(this.[%FieldName%]OrgStr)){
        Matcher [%FieldMapName%]Matcher = pattern.matcher(this.[%FieldName%]OrgStr);
        String[] [%FieldMapName%] = [%FieldMapName%]Matcher.replaceAll("").split("[%SplitCode1%]");
        this.[%FieldName%] = new HashMap<>();
        for (String item : [%FieldMapName%]) {
            String[] tempArr = item.split("[%SplitCode2%]");
            if (tempArr.length >= 2) {
                String key = tempArr[0];
                [%FieldType%] val = [%ParseFun%];
                this.[%FieldName%].put(key, val);                
            }
        }
     }   
    """

    MAP_2D_PARSE_CODE: str = """
    if(this.[%FieldName%]OrgStr != null  && !"".equals(this.[%FieldName%]OrgStr)){
        Matcher [%FieldMapName%]Matcher = pattern.matcher(this.[%FieldName%]OrgStr);
        String[] [%FieldMapName%] = [%FieldMapName%]Matcher.replaceAll("").split("[%SplitCode1%]");
        this.[%FieldName%] = new HashMap<>();
        for (String item : [%FieldMapName%]) {
            String[] tempArr = item.split("[%SplitCode2%]");
            if (tempArr.length >= 2) {                
                String key = tempArr[0];
                String[] ts = tempArr[1].split(",");
                [%FieldType%][] ls = new [%FieldType%][ts.length];
                for (int i = 0; i < ts.length; i++) {
                    ls[i] = [%ParseFun2D%];
                }
                this.[%FieldName%].put(key, ls);               
            }
        }
    }
    """

    def genMapParseCode(self, field: FieldData):
        code = ""
        # code += "\n        this." + field.fieldKey + "OrgStr" + " = json[\"" + field.fieldKey + "\"]"
        tmpSetter: str
        if field.fieldType.is2DMapType():
            tmpSetter = self.MAP_2D_PARSE_CODE
        else:
            tmpSetter = self.MAP_PARSE_CODE

        fieldMapName = field.fieldKey + "OrgStrMap"
        tmpSetter = tmpSetter.replace("[%FieldType%]", self.getFieldRealTypeArrOrMap(field.fieldType))
        tmpSetter = tmpSetter.replace("[%FieldName%]", field.fieldKey)
        tmpSetter = tmpSetter.replace("[%FieldMapName%]", fieldMapName)
        tmpSetter = tmpSetter.replace("[%SplitCode1%]", ";")
        tmpSetter = tmpSetter.replace("[%SplitCode2%]", "=")
        if field.fieldType.isArrIntType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "Float.valueOf(tempArr[1].trim()).intValue()")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "Float.valueOf(ts[i].trim()).intValue()")
        elif field.fieldType.isArrFloatType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "Float.valueOf(tempArr[1].trim())")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "Float.valueOf(ts[i].trim())")
        elif field.fieldType.isArrDoubleType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "Double.valueOf(tempArr[1].trim())")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "Double.valueOf(ts[i].trim())")
        elif field.fieldType.isArrStrType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "tempArr[1]")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "ts[i]")
        elif field.fieldType.isArrBoolType():
            tmpSetter = tmpSetter.replace("[%ParseFun%]", "\"1\".equals(tempArr[1].trim()) || "
                                          + "\"t\".equals(tempArr[1].trim().toLowerCase()) || "
                                          + "\"true\".equals(tempArr[1].trim().toLowerCase())")
            tmpSetter = tmpSetter.replace("[%ParseFun2D%]", "\"1\".equals(ts[i].trim()) || "
                                          + "\"t\".equals(ts[i].trim().toLowerCase()) || "
                                          + "\"true\".equals(ts[i].trim().toLowerCase())")
        code += tmpSetter
        return code